const uuid = require('uuid');
const awsClients = require('./aws-clients');
const Config = require('./config.js');
const { UploadPdfError, StateMachineExecutionError, SetStatusError, IngestApplicationError } = require('./errors.js');
const { PutObjectCommand } = require('@aws-sdk/client-s3');
const { StartExecutionCommand } = require('@aws-sdk/client-sfn');
const { PutCommand } = require('@aws-sdk/lib-dynamodb');
const { getLogger } = require('./logging.js');

const logger = getLogger(Config.LOG_LEVEL);

class Application {
  constructor (applicationDocument, clientName, clientId, receivedDateTime, submittingPartner, forms, eventSource) {
    this.applicationId = uuid.v1(new Date().toISOString());
    this.applicationDocument = applicationDocument;
    this.clientName = clientName;
    this.clientId = clientId;
    this.receivedDateTime = receivedDateTime;
    this.submittingPartner = submittingPartner;
    this.forms = forms;
    this.applicationS3Key = this.getApplicationS3Key();
    this.eventSource = eventSource;
  }

  async commit () {
    try {
      await this.uploadApplication();
      const executionArn = await this.startStateMachine();
      await this.setStatus(executionArn);
    } catch (error) {
      const message = `An error occurred committing application ${this.applicationId}.  Error:  ${error.message}`;
      logger.error(message);
      throw new IngestApplicationError(message);
    }
  }

  async uploadApplication () {
    try {
      const body = Buffer.from(this.applicationDocument.replace(/^data:application\/\w+;base64,/, '')
        , 'base64');
      const params = {
        'Body': body,
        'ContentEncoding': 'base64',
        'ContentType': 'application/pdf',
        'Bucket': Config.S3_DATA_BUCKET,
        'Key': this.applicationS3Key
      };
      const command = new PutObjectCommand(params);

      await awsClients.s3Client.send(command);
    } catch (error) {
      const message = `An error occurred uploading application ${this.applicationS3Key} 
                to ${Config.S3_DATA_BUCKET}.  Error:  ${error.message}`;
      logger.error(message);
      throw new UploadPdfError(message);
    }
  }

  async startStateMachine () {
    try {
      const stateMachineInput = {
        'submittingPartner': this.submittingPartner,
        'clientId': this.clientId,
        'sourceOfWork': this.clientName,
        'storedPdfKey': this.applicationS3Key,
        'applicationId': this.applicationId,
        'forms': this.forms
      };
      const params = {
        'stateMachineArn': Config.STATE_MACHINE_ARN,
        'input': JSON.stringify(stateMachineInput)
      };
      const command = new StartExecutionCommand(params);

      const response = await awsClients.stepFunctionsClient.send(command);
      return response.executionArn;
    } catch (error) {
      const message = `An error occurred starting state machine ${Config.STATE_MACHINE_ARN}.  Error: ${error.message}`;
      logger.error(message);
      throw new StateMachineExecutionError(message);
    }
  }

  async setStatus (executionArn) {
    try {
      const params = {
        'TableName': Config.APPLICATION_STATUS_TABLE,
        'Item': {
          'ApplicationId': this.applicationId,
          'EventSource': this.eventSource,
          'ExecutionArn': executionArn,
          'Timestamp': new Date().toISOString(),
          'ClientId': this.clientId,
          'Status': 'Received'
        }
      };
      const command = new PutCommand(params);
      await awsClients.dynamoDBClient.send(command);
    } catch (error) {
      const message = `An error occurred setting status in ${Config.APPLICATION_STATUS_TABLE}.  Error: ${error.message}`;
      logger.error(message);
      throw new SetStatusError(message);
    }
  }

  getApplicationS3Key () {
    const date = new Date(this.receivedDateTime);
    const year = date.getFullYear().toString();
    const month = (date.getMonth() + 1).toString().padStart(2, '0');
    const day = date.getDate().toString().padStart(2, '0');

    const key = `api/onboard/applications/${this.clientId}/${year}/${month}/${day}/${this.applicationId}.pdf`;

    return key;
  }
}

exports.Application = Application;
